use serde::{
    Deserialize,
    Serialize,
};
pub struct Menu {
    pub input: &'static str,
    pub opcode: &'static str,
    pub default: &'static str,
    pub field: &'static str,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum UnOp {
    Not,
    Length,
    Round,
    Abs,
    Floor,
    Ceil,
    Sqrt,
    Sin,
    Cos,
    Tan,
    Asin,
    Acos,
    Atan,
    Ln,
    Log,
    AntiLn,
    AntiLog,
    Minus,
}

impl UnOp {
    pub fn opcode(&self) -> &'static str {
        match self {
            Self::Not => "operator_not",
            Self::Length => "operator_length",
            Self::Round => "operator_round",
            Self::Abs => "operator_mathop",
            Self::Floor => "operator_mathop",
            Self::Ceil => "operator_mathop",
            Self::Sqrt => "operator_mathop",
            Self::Sin => "operator_mathop",
            Self::Cos => "operator_mathop",
            Self::Tan => "operator_mathop",
            Self::Asin => "operator_mathop",
            Self::Acos => "operator_mathop",
            Self::Atan => "operator_mathop",
            Self::Ln => "operator_mathop",
            Self::Log => "operator_mathop",
            Self::AntiLn => "operator_mathop",
            Self::AntiLog => "operator_mathop",
            _ => unreachable!(),
        }
    }

    pub fn input(&self) -> &'static str {
        match self {
            Self::Not => "OPERAND",
            Self::Length => "STRING",
            Self::Round => "NUM",
            Self::Abs => "NUM",
            Self::Floor => "NUM",
            Self::Ceil => "NUM",
            Self::Sqrt => "NUM",
            Self::Sin => "NUM",
            Self::Cos => "NUM",
            Self::Tan => "NUM",
            Self::Asin => "NUM",
            Self::Acos => "NUM",
            Self::Atan => "NUM",
            Self::Ln => "NUM",
            Self::Log => "NUM",
            Self::AntiLn => "NUM",
            Self::AntiLog => "NUM",
            _ => unreachable!(),
        }
    }

    pub fn fields(&self) -> Option<&'static str> {
        match self {
            Self::Not => None,
            Self::Length => None,
            Self::Round => None,
            Self::Abs => Some("{\"OPERATOR\": [\"abs\", null]}"),
            Self::Floor => Some("{\"OPERATOR\": [\"floor\", null]}"),
            Self::Ceil => Some("{\"OPERATOR\": [\"ceiling\", null]}"),
            Self::Sqrt => Some("{\"OPERATOR\": [\"sqrt\", null]}"),
            Self::Sin => Some("{\"OPERATOR\": [\"sin\", null]}"),
            Self::Cos => Some("{\"OPERATOR\": [\"cos\", null]}"),
            Self::Tan => Some("{\"OPERATOR\": [\"tan\", null]}"),
            Self::Asin => Some("{\"OPERATOR\": [\"asin\", null]}"),
            Self::Acos => Some("{\"OPERATOR\": [\"acos\", null]}"),
            Self::Atan => Some("{\"OPERATOR\": [\"atan\", null]}"),
            Self::Ln => Some("{\"OPERATOR\": [\"ln\", null]}"),
            Self::Log => Some("{\"OPERATOR\": [\"log\", null]}"),
            Self::AntiLn => Some("{\"OPERATOR\": [\"e ^\", null]}"),
            Self::AntiLog => Some("{\"OPERATOR\": [\"10 ^\", null]}"),
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum BinOp {
    Add,
    Sub,
    Mul,
    Div,
    Mod,
    Lt,
    Gt,
    Eq,
    And,
    Or,
    Join,
    In,
    Of,
    Le,
    Ge,
    Ne,
    FloorDiv,
}

impl BinOp {
    pub fn opcode(&self) -> &'static str {
        match self {
            Self::Add => "operator_add",
            Self::Sub => "operator_subtract",
            Self::Mul => "operator_multiply",
            Self::Div => "operator_divide",
            Self::Mod => "operator_mod",
            Self::Lt => "operator_lt",
            Self::Gt => "operator_gt",
            Self::Eq => "operator_equals",
            Self::And => "operator_and",
            Self::Or => "operator_or",
            Self::Join => "operator_join",
            Self::In => "operator_contains",
            Self::Of => "operator_letter_of",
            _ => unreachable!(),
        }
    }

    pub fn lhs(&self) -> &'static str {
        match self {
            Self::Add => "NUM1",
            Self::Sub => "NUM1",
            Self::Mul => "NUM1",
            Self::Div => "NUM1",
            Self::Mod => "NUM1",
            Self::Lt => "OPERAND1",
            Self::Gt => "OPERAND1",
            Self::Eq => "OPERAND1",
            Self::And => "OPERAND1",
            Self::Or => "OPERAND1",
            Self::Join => "STRING1",
            Self::In => "STRING2",
            Self::Of => "STRING",
            _ => unreachable!(),
        }
    }

    pub fn rhs(&self) -> &'static str {
        match self {
            Self::Add => "NUM2",
            Self::Sub => "NUM2",
            Self::Mul => "NUM2",
            Self::Div => "NUM2",
            Self::Mod => "NUM2",
            Self::Lt => "OPERAND2",
            Self::Gt => "OPERAND2",
            Self::Eq => "OPERAND2",
            Self::And => "OPERAND2",
            Self::Or => "OPERAND2",
            Self::Join => "STRING2",
            Self::In => "STRING1",
            Self::Of => "LETTER",
            _ => unreachable!(),
        }
    }
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum Block {
    Move,
    TurnLeft,
    TurnRight,
    GotoRandomPosition,
    GotoMousePointer,
    Goto1,
    Goto2,
    Glide3,
    Glide2,
    GlideToRandomPosition,
    GlideToMousePointer,
    PointInDirection,
    PointTowardsMousePointer,
    PointTowardsRandomDirection,
    PointTowards,
    ChangeX,
    SetX,
    ChangeY,
    SetY,
    IfOnEdgeBounce,
    SetRotationStyleLeftRight,
    SetRotationStyleDoNotRotate,
    SetRotationStyleAllAround,
    Say2,
    Say1,
    Think2,
    Think1,
    SwitchCostume,
    NextCostume,
    SwitchBackdrop,
    PreviousBackdrop,
    RandomBackdrop,
    NextBackdrop,
    SetSize,
    ChangeSize,
    ChangeColorEffect,
    ChangeFisheyeEffect,
    ChangeWhirlEffect,
    ChangePixelateEffect,
    ChangeMosaicEffect,
    ChangeBrightnessEffect,
    ChangeGhostEffect,
    SetColorEffect,
    SetFisheyeEffect,
    SetWhirlEffect,
    SetPixelateEffect,
    SetMosaicEffect,
    SetBrightnessEffect,
    SetGhostEffect,
    ClearGraphicEffects,
    Show,
    Hide,
    GotoFront,
    GotoBack,
    GoForward,
    GoBackward,
    PlaySoundUntilDone,
    StartSound,
    StopAllSounds,
    ChangePitchEffect,
    ChangePanEffect,
    SetPitchEffect,
    SetPanEffect,
    ChangeVolume,
    SetVolume,
    ClearSoundEffects,
    Broadcast,
    BroadcastAndWait,
    Wait,
    StopAll,
    StopThisScript,
    StopOtherScripts,
    DeleteThisClone,
    Clone0,
    Clone1,
    Ask,
    SetDragModeDraggable,
    SetDragModeNotDraggable,
    ResetTimer,
    EraseAll,
    Stamp,
    PenDown,
    PenUp,
    SetPenColor,
    ChangePenSize,
    SetPenSize,
    SetPenHue,
    SetPenSaturation,
    SetPenBrightness,
    SetPenTransparency,
    ChangePenHue,
    ChangePenSaturation,
    ChangePenBrightness,
    ChangePenTransparency,
    Rest,
    SetTempo,
    ChangeTempo,
}

impl Block {
    pub fn menu(&self) -> Option<Menu> {
        match self {
            Self::GotoRandomPosition => Some(Menu {
                opcode: "motion_goto_menu",
                input: "TO",
                field: "TO",
                default: "_random_",
            }),
            Self::GotoMousePointer => Some(Menu {
                opcode: "motion_goto_menu",
                input: "TO",
                field: "TO",
                default: "_mouse_",
            }),
            Self::Goto1 => Some(Menu {
                opcode: "motion_goto_menu",
                input: "TO",
                field: "TO",
                default: "_random_",
            }),
            Self::Glide2 => Some(Menu {
                opcode: "motion_glideto_menu",
                input: "TO",
                field: "TO",
                default: "_random_",
            }),
            Self::GlideToRandomPosition => Some(Menu {
                opcode: "motion_glideto_menu",
                input: "TO",
                field: "TO",
                default: "_random_",
            }),
            Self::GlideToMousePointer => Some(Menu {
                opcode: "motion_glideto_menu",
                input: "TO",
                field: "TO",
                default: "_mouse_",
            }),
            Self::PointTowardsMousePointer => Some(Menu {
                opcode: "motion_pointtowards_menu",
                input: "TOWARDS",
                field: "TOWARDS",
                default: "_mouse_",
            }),
            Self::PointTowardsRandomDirection => Some(Menu {
                opcode: "motion_pointtowards_menu",
                input: "TOWARDS",
                field: "TOWARDS",
                default: "_random_",
            }),
            Self::PointTowards => Some(Menu {
                opcode: "motion_pointtowards_menu",
                input: "TOWARDS",
                field: "TOWARDS",
                default: "_random_",
            }),
            Self::SwitchCostume => Some(Menu {
                opcode: "looks_costume",
                input: "COSTUME",
                field: "COSTUME",
                default: "make gh issue if this bothers u",
            }),
            Self::SwitchBackdrop => Some(Menu {
                opcode: "looks_backdrops",
                input: "BACKDROP",
                field: "BACKDROP",
                default: "next backdrop",
            }),
            Self::PreviousBackdrop => Some(Menu {
                opcode: "looks_backdrops",
                input: "BACKDROP",
                field: "BACKDROP",
                default: "previous backdrop",
            }),
            Self::RandomBackdrop => Some(Menu {
                opcode: "looks_backdrops",
                input: "BACKDROP",
                field: "BACKDROP",
                default: "random backdrop",
            }),
            Self::PlaySoundUntilDone => Some(Menu {
                opcode: "sound_sounds_menu",
                input: "SOUND_MENU",
                field: "SOUND_MENU",
                default: "make gh issue if this bothers u",
            }),
            Self::StartSound => Some(Menu {
                opcode: "sound_sounds_menu",
                input: "SOUND_MENU",
                field: "SOUND_MENU",
                default: "make gh issue if this bothers u",
            }),
            Self::Clone0 => Some(Menu {
                opcode: "control_create_clone_of_menu",
                input: "CLONE_OPTION",
                field: "CLONE_OPTION",
                default: "_myself_",
            }),
            Self::Clone1 => Some(Menu {
                opcode: "control_create_clone_of_menu",
                input: "CLONE_OPTION",
                field: "CLONE_OPTION",
                default: "_myself_",
            }),
            Self::SetPenHue => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "color",
            }),
            Self::SetPenSaturation => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "saturation",
            }),
            Self::SetPenBrightness => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "brightness",
            }),
            Self::SetPenTransparency => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "transparency",
            }),
            Self::ChangePenHue => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "color",
            }),
            Self::ChangePenSaturation => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "saturation",
            }),
            Self::ChangePenBrightness => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "brightness",
            }),
            Self::ChangePenTransparency => Some(Menu {
                opcode: "pen_menu_colorParam",
                input: "COLOR_PARAM",
                field: "colorParam",
                default: "transparency",
            }),
            _ => None,
        }
    }

    pub fn overloads(name: &str) -> &'static [Self] {
        match name {
            "goto" => &[Self::Goto1, Self::Goto2],
            "glide" => &[Self::Glide3, Self::Glide2],
            "say" => &[Self::Say2, Self::Say1],
            "think" => &[Self::Think2, Self::Think1],
            "clone" => &[Self::Clone0, Self::Clone1],
            _ => &[],
        }
    }

    pub fn from_shape(name: &str, args: usize) -> Option<Self> {
        match (name, args) {
            ("move", _) => Some(Self::Move),
            ("turn_left", _) => Some(Self::TurnLeft),
            ("turn_right", _) => Some(Self::TurnRight),
            ("goto_random_position", _) => Some(Self::GotoRandomPosition),
            ("goto_mouse_pointer", _) => Some(Self::GotoMousePointer),
            ("goto", 1) => Some(Self::Goto1),
            ("goto", 2) => Some(Self::Goto2),
            ("goto", _) => Some(Self::Goto1),
            ("glide", 3) => Some(Self::Glide3),
            ("glide", 2) => Some(Self::Glide2),
            ("glide", _) => Some(Self::Glide3),
            ("glide_to_random_position", _) => Some(Self::GlideToRandomPosition),
            ("glide_to_mouse_pointer", _) => Some(Self::GlideToMousePointer),
            ("point_in_direction", _) => Some(Self::PointInDirection),
            ("point_towards_mouse_pointer", _) => Some(Self::PointTowardsMousePointer),
            ("point_towards_random_direction", _) => Some(Self::PointTowardsRandomDirection),
            ("point_towards", _) => Some(Self::PointTowards),
            ("change_x", _) => Some(Self::ChangeX),
            ("set_x", _) => Some(Self::SetX),
            ("change_y", _) => Some(Self::ChangeY),
            ("set_y", _) => Some(Self::SetY),
            ("if_on_edge_bounce", _) => Some(Self::IfOnEdgeBounce),
            ("set_rotation_style_left_right", _) => Some(Self::SetRotationStyleLeftRight),
            ("set_rotation_style_do_not_rotate", _) => Some(Self::SetRotationStyleDoNotRotate),
            ("set_rotation_style_all_around", _) => Some(Self::SetRotationStyleAllAround),
            ("say", 2) => Some(Self::Say2),
            ("say", 1) => Some(Self::Say1),
            ("say", _) => Some(Self::Say2),
            ("think", 2) => Some(Self::Think2),
            ("think", 1) => Some(Self::Think1),
            ("think", _) => Some(Self::Think2),
            ("switch_costume", _) => Some(Self::SwitchCostume),
            ("next_costume", _) => Some(Self::NextCostume),
            ("switch_backdrop", _) => Some(Self::SwitchBackdrop),
            ("previous_backdrop", _) => Some(Self::PreviousBackdrop),
            ("random_backdrop", _) => Some(Self::RandomBackdrop),
            ("next_backdrop", _) => Some(Self::NextBackdrop),
            ("set_size", _) => Some(Self::SetSize),
            ("change_size", _) => Some(Self::ChangeSize),
            ("change_color_effect", _) => Some(Self::ChangeColorEffect),
            ("change_fisheye_effect", _) => Some(Self::ChangeFisheyeEffect),
            ("change_whirl_effect", _) => Some(Self::ChangeWhirlEffect),
            ("change_pixelate_effect", _) => Some(Self::ChangePixelateEffect),
            ("change_mosaic_effect", _) => Some(Self::ChangeMosaicEffect),
            ("change_brightness_effect", _) => Some(Self::ChangeBrightnessEffect),
            ("change_ghost_effect", _) => Some(Self::ChangeGhostEffect),
            ("set_color_effect", _) => Some(Self::SetColorEffect),
            ("set_fisheye_effect", _) => Some(Self::SetFisheyeEffect),
            ("set_whirl_effect", _) => Some(Self::SetWhirlEffect),
            ("set_pixelate_effect", _) => Some(Self::SetPixelateEffect),
            ("set_mosaic_effect", _) => Some(Self::SetMosaicEffect),
            ("set_brightness_effect", _) => Some(Self::SetBrightnessEffect),
            ("set_ghost_effect", _) => Some(Self::SetGhostEffect),
            ("clear_graphic_effects", _) => Some(Self::ClearGraphicEffects),
            ("show", _) => Some(Self::Show),
            ("hide", _) => Some(Self::Hide),
            ("goto_front", _) => Some(Self::GotoFront),
            ("goto_back", _) => Some(Self::GotoBack),
            ("go_forward", _) => Some(Self::GoForward),
            ("go_backward", _) => Some(Self::GoBackward),
            ("play_sound_until_done", _) => Some(Self::PlaySoundUntilDone),
            ("start_sound", _) => Some(Self::StartSound),
            ("stop_all_sounds", _) => Some(Self::StopAllSounds),
            ("change_pitch_effect", _) => Some(Self::ChangePitchEffect),
            ("change_pan_effect", _) => Some(Self::ChangePanEffect),
            ("set_pitch_effect", _) => Some(Self::SetPitchEffect),
            ("set_pan_effect", _) => Some(Self::SetPanEffect),
            ("change_volume", _) => Some(Self::ChangeVolume),
            ("set_volume", _) => Some(Self::SetVolume),
            ("clear_sound_effects", _) => Some(Self::ClearSoundEffects),
            ("broadcast", _) => Some(Self::Broadcast),
            ("broadcast_and_wait", _) => Some(Self::BroadcastAndWait),
            ("wait", _) => Some(Self::Wait),
            ("stop_all", _) => Some(Self::StopAll),
            ("stop_this_script", _) => Some(Self::StopThisScript),
            ("stop_other_scripts", _) => Some(Self::StopOtherScripts),
            ("delete_this_clone", _) => Some(Self::DeleteThisClone),
            ("clone", 0) => Some(Self::Clone0),
            ("clone", 1) => Some(Self::Clone1),
            ("clone", _) => Some(Self::Clone0),
            ("ask", _) => Some(Self::Ask),
            ("set_drag_mode_draggable", _) => Some(Self::SetDragModeDraggable),
            ("set_drag_mode_not_draggable", _) => Some(Self::SetDragModeNotDraggable),
            ("reset_timer", _) => Some(Self::ResetTimer),
            ("erase_all", _) => Some(Self::EraseAll),
            ("stamp", _) => Some(Self::Stamp),
            ("pen_down", _) => Some(Self::PenDown),
            ("pen_up", _) => Some(Self::PenUp),
            ("set_pen_color", _) => Some(Self::SetPenColor),
            ("change_pen_size", _) => Some(Self::ChangePenSize),
            ("set_pen_size", _) => Some(Self::SetPenSize),
            ("set_pen_hue", _) => Some(Self::SetPenHue),
            ("set_pen_saturation", _) => Some(Self::SetPenSaturation),
            ("set_pen_brightness", _) => Some(Self::SetPenBrightness),
            ("set_pen_transparency", _) => Some(Self::SetPenTransparency),
            ("change_pen_hue", _) => Some(Self::ChangePenHue),
            ("change_pen_saturation", _) => Some(Self::ChangePenSaturation),
            ("change_pen_brightness", _) => Some(Self::ChangePenBrightness),
            ("change_pen_transparency", _) => Some(Self::ChangePenTransparency),
            ("rest", _) => Some(Self::Rest),
            ("set_tempo", _) => Some(Self::SetTempo),
            ("change_tempo", _) => Some(Self::ChangeTempo),
            _ => None,
        }
    }

    pub fn name(&self) -> &'static str {
        match self {
            Self::Move => "move",
            Self::TurnLeft => "turn_left",
            Self::TurnRight => "turn_right",
            Self::GotoRandomPosition => "goto_random_position",
            Self::GotoMousePointer => "goto_mouse_pointer",
            Self::Goto1 => "goto",
            Self::Goto2 => "goto",
            Self::Glide3 => "glide",
            Self::Glide2 => "glide",
            Self::GlideToRandomPosition => "glide_to_random_position",
            Self::GlideToMousePointer => "glide_to_mouse_pointer",
            Self::PointInDirection => "point_in_direction",
            Self::PointTowardsMousePointer => "point_towards_mouse_pointer",
            Self::PointTowardsRandomDirection => "point_towards_random_direction",
            Self::PointTowards => "point_towards",
            Self::ChangeX => "change_x",
            Self::SetX => "set_x",
            Self::ChangeY => "change_y",
            Self::SetY => "set_y",
            Self::IfOnEdgeBounce => "if_on_edge_bounce",
            Self::SetRotationStyleLeftRight => "set_rotation_style_left_right",
            Self::SetRotationStyleDoNotRotate => "set_rotation_style_do_not_rotate",
            Self::SetRotationStyleAllAround => "set_rotation_style_all_around",
            Self::Say2 => "say",
            Self::Say1 => "say",
            Self::Think2 => "think",
            Self::Think1 => "think",
            Self::SwitchCostume => "switch_costume",
            Self::NextCostume => "next_costume",
            Self::SwitchBackdrop => "switch_backdrop",
            Self::PreviousBackdrop => "previous_backdrop",
            Self::RandomBackdrop => "random_backdrop",
            Self::NextBackdrop => "next_backdrop",
            Self::SetSize => "set_size",
            Self::ChangeSize => "change_size",
            Self::ChangeColorEffect => "change_color_effect",
            Self::ChangeFisheyeEffect => "change_fisheye_effect",
            Self::ChangeWhirlEffect => "change_whirl_effect",
            Self::ChangePixelateEffect => "change_pixelate_effect",
            Self::ChangeMosaicEffect => "change_mosaic_effect",
            Self::ChangeBrightnessEffect => "change_brightness_effect",
            Self::ChangeGhostEffect => "change_ghost_effect",
            Self::SetColorEffect => "set_color_effect",
            Self::SetFisheyeEffect => "set_fisheye_effect",
            Self::SetWhirlEffect => "set_whirl_effect",
            Self::SetPixelateEffect => "set_pixelate_effect",
            Self::SetMosaicEffect => "set_mosaic_effect",
            Self::SetBrightnessEffect => "set_brightness_effect",
            Self::SetGhostEffect => "set_ghost_effect",
            Self::ClearGraphicEffects => "clear_graphic_effects",
            Self::Show => "show",
            Self::Hide => "hide",
            Self::GotoFront => "goto_front",
            Self::GotoBack => "goto_back",
            Self::GoForward => "go_forward",
            Self::GoBackward => "go_backward",
            Self::PlaySoundUntilDone => "play_sound_until_done",
            Self::StartSound => "start_sound",
            Self::StopAllSounds => "stop_all_sounds",
            Self::ChangePitchEffect => "change_pitch_effect",
            Self::ChangePanEffect => "change_pan_effect",
            Self::SetPitchEffect => "set_pitch_effect",
            Self::SetPanEffect => "set_pan_effect",
            Self::ChangeVolume => "change_volume",
            Self::SetVolume => "set_volume",
            Self::ClearSoundEffects => "clear_sound_effects",
            Self::Broadcast => "broadcast",
            Self::BroadcastAndWait => "broadcast_and_wait",
            Self::Wait => "wait",
            Self::StopAll => "stop_all",
            Self::StopThisScript => "stop_this_script",
            Self::StopOtherScripts => "stop_other_scripts",
            Self::DeleteThisClone => "delete_this_clone",
            Self::Clone0 => "clone",
            Self::Clone1 => "clone",
            Self::Ask => "ask",
            Self::SetDragModeDraggable => "set_drag_mode_draggable",
            Self::SetDragModeNotDraggable => "set_drag_mode_not_draggable",
            Self::ResetTimer => "reset_timer",
            Self::EraseAll => "erase_all",
            Self::Stamp => "stamp",
            Self::PenDown => "pen_down",
            Self::PenUp => "pen_up",
            Self::SetPenColor => "set_pen_color",
            Self::ChangePenSize => "change_pen_size",
            Self::SetPenSize => "set_pen_size",
            Self::SetPenHue => "set_pen_hue",
            Self::SetPenSaturation => "set_pen_saturation",
            Self::SetPenBrightness => "set_pen_brightness",
            Self::SetPenTransparency => "set_pen_transparency",
            Self::ChangePenHue => "change_pen_hue",
            Self::ChangePenSaturation => "change_pen_saturation",
            Self::ChangePenBrightness => "change_pen_brightness",
            Self::ChangePenTransparency => "change_pen_transparency",
            Self::Rest => "rest",
            Self::SetTempo => "set_tempo",
            Self::ChangeTempo => "change_tempo",
        }
    }

    pub fn all_names() -> &'static [&'static str] {
        &[
            "move",
            "turn_left",
            "turn_right",
            "goto_random_position",
            "goto_mouse_pointer",
            "goto",
            "glide",
            "glide_to_random_position",
            "glide_to_mouse_pointer",
            "point_in_direction",
            "point_towards_mouse_pointer",
            "point_towards_random_direction",
            "point_towards",
            "change_x",
            "set_x",
            "change_y",
            "set_y",
            "if_on_edge_bounce",
            "set_rotation_style_left_right",
            "set_rotation_style_do_not_rotate",
            "set_rotation_style_all_around",
            "say",
            "think",
            "switch_costume",
            "next_costume",
            "switch_backdrop",
            "previous_backdrop",
            "random_backdrop",
            "next_backdrop",
            "set_size",
            "change_size",
            "change_color_effect",
            "change_fisheye_effect",
            "change_whirl_effect",
            "change_pixelate_effect",
            "change_mosaic_effect",
            "change_brightness_effect",
            "change_ghost_effect",
            "set_color_effect",
            "set_fisheye_effect",
            "set_whirl_effect",
            "set_pixelate_effect",
            "set_mosaic_effect",
            "set_brightness_effect",
            "set_ghost_effect",
            "clear_graphic_effects",
            "show",
            "hide",
            "goto_front",
            "goto_back",
            "go_forward",
            "go_backward",
            "play_sound_until_done",
            "start_sound",
            "stop_all_sounds",
            "change_pitch_effect",
            "change_pan_effect",
            "set_pitch_effect",
            "set_pan_effect",
            "change_volume",
            "set_volume",
            "clear_sound_effects",
            "broadcast",
            "broadcast_and_wait",
            "wait",
            "stop_all",
            "stop_this_script",
            "stop_other_scripts",
            "delete_this_clone",
            "clone",
            "ask",
            "set_drag_mode_draggable",
            "set_drag_mode_not_draggable",
            "reset_timer",
            "erase_all",
            "stamp",
            "pen_down",
            "pen_up",
            "set_pen_color",
            "change_pen_size",
            "set_pen_size",
            "set_pen_hue",
            "set_pen_saturation",
            "set_pen_brightness",
            "set_pen_transparency",
            "change_pen_hue",
            "change_pen_saturation",
            "change_pen_brightness",
            "change_pen_transparency",
            "rest",
            "set_tempo",
            "change_tempo",
        ]
    }

    pub fn opcode(&self) -> &'static str {
        match self {
            Self::Move => "motion_movesteps",
            Self::TurnLeft => "motion_turnleft",
            Self::TurnRight => "motion_turnright",
            Self::GotoRandomPosition => "motion_goto",
            Self::GotoMousePointer => "motion_goto",
            Self::Goto1 => "motion_goto",
            Self::Goto2 => "motion_gotoxy",
            Self::Glide3 => "motion_glidesecstoxy",
            Self::Glide2 => "motion_glideto",
            Self::GlideToRandomPosition => "motion_glideto",
            Self::GlideToMousePointer => "motion_glideto",
            Self::PointInDirection => "motion_pointindirection",
            Self::PointTowardsMousePointer => "motion_pointtowards",
            Self::PointTowardsRandomDirection => "motion_pointtowards",
            Self::PointTowards => "motion_pointtowards",
            Self::ChangeX => "motion_changexby",
            Self::SetX => "motion_setx",
            Self::ChangeY => "motion_changeyby",
            Self::SetY => "motion_sety",
            Self::IfOnEdgeBounce => "motion_ifonedgebounce",
            Self::SetRotationStyleLeftRight => "motion_setrotationstyle",
            Self::SetRotationStyleDoNotRotate => "motion_setrotationstyle",
            Self::SetRotationStyleAllAround => "motion_setrotationstyle",
            Self::Say2 => "looks_sayforsecs",
            Self::Say1 => "looks_say",
            Self::Think2 => "looks_thinkforsecs",
            Self::Think1 => "looks_think",
            Self::SwitchCostume => "looks_switchcostumeto",
            Self::NextCostume => "looks_nextcostume",
            Self::SwitchBackdrop => "looks_switchbackdropto",
            Self::PreviousBackdrop => "looks_switchbackdropto",
            Self::RandomBackdrop => "looks_switchbackdropto",
            Self::NextBackdrop => "looks_nextbackdrop",
            Self::SetSize => "looks_setsizeto",
            Self::ChangeSize => "looks_changesizeby",
            Self::ChangeColorEffect => "looks_changeeffectby",
            Self::ChangeFisheyeEffect => "looks_changeeffectby",
            Self::ChangeWhirlEffect => "looks_changeeffectby",
            Self::ChangePixelateEffect => "looks_changeeffectby",
            Self::ChangeMosaicEffect => "looks_changeeffectby",
            Self::ChangeBrightnessEffect => "looks_changeeffectby",
            Self::ChangeGhostEffect => "looks_changeeffectby",
            Self::SetColorEffect => "looks_seteffectto",
            Self::SetFisheyeEffect => "looks_seteffectto",
            Self::SetWhirlEffect => "looks_seteffectto",
            Self::SetPixelateEffect => "looks_seteffectto",
            Self::SetMosaicEffect => "looks_seteffectto",
            Self::SetBrightnessEffect => "looks_seteffectto",
            Self::SetGhostEffect => "looks_seteffectto",
            Self::ClearGraphicEffects => "looks_cleargraphiceffects",
            Self::Show => "looks_show",
            Self::Hide => "looks_hide",
            Self::GotoFront => "looks_gotofrontback",
            Self::GotoBack => "looks_gotofrontback",
            Self::GoForward => "looks_goforwardbackwardlayers",
            Self::GoBackward => "looks_goforwardbackwardlayers",
            Self::PlaySoundUntilDone => "sound_playuntildone",
            Self::StartSound => "sound_play",
            Self::StopAllSounds => "sound_stopallsounds",
            Self::ChangePitchEffect => "sound_changeeffectby",
            Self::ChangePanEffect => "sound_changeeffectby",
            Self::SetPitchEffect => "sound_seteffectto",
            Self::SetPanEffect => "sound_seteffectto",
            Self::ChangeVolume => "sound_changevolumeby",
            Self::SetVolume => "sound_setvolumeto",
            Self::ClearSoundEffects => "sound_cleareffects",
            Self::Broadcast => "event_broadcast",
            Self::BroadcastAndWait => "event_broadcastandwait",
            Self::Wait => "control_wait",
            Self::StopAll => "control_stop",
            Self::StopThisScript => "control_stop",
            Self::StopOtherScripts => "control_stop",
            Self::DeleteThisClone => "control_delete_this_clone",
            Self::Clone0 => "control_create_clone_of",
            Self::Clone1 => "control_create_clone_of",
            Self::Ask => "sensing_askandwait",
            Self::SetDragModeDraggable => "sensing_setdragmode",
            Self::SetDragModeNotDraggable => "sensing_setdragmode",
            Self::ResetTimer => "sensing_resettimer",
            Self::EraseAll => "pen_clear",
            Self::Stamp => "pen_stamp",
            Self::PenDown => "pen_penDown",
            Self::PenUp => "pen_penUp",
            Self::SetPenColor => "pen_setPenColorToColor",
            Self::ChangePenSize => "pen_changePenSizeBy",
            Self::SetPenSize => "pen_setPenSizeTo",
            Self::SetPenHue => "pen_setPenColorParamTo",
            Self::SetPenSaturation => "pen_setPenColorParamTo",
            Self::SetPenBrightness => "pen_setPenColorParamTo",
            Self::SetPenTransparency => "pen_setPenColorParamTo",
            Self::ChangePenHue => "pen_changePenColorParamBy",
            Self::ChangePenSaturation => "pen_changePenColorParamBy",
            Self::ChangePenBrightness => "pen_changePenColorParamBy",
            Self::ChangePenTransparency => "pen_changePenColorParamBy",
            Self::Rest => "music_restForBeats",
            Self::SetTempo => "music_setTempo",
            Self::ChangeTempo => "music_changeTempo",
        }
    }

    pub fn args(&self) -> &'static [&'static str] {
        match self {
            Self::Move => &["STEPS"],
            Self::TurnLeft => &["DEGREES"],
            Self::TurnRight => &["DEGREES"],
            Self::GotoRandomPosition => &[],
            Self::GotoMousePointer => &[],
            Self::Goto1 => &["TO"],
            Self::Goto2 => &["X", "Y"],
            Self::Glide3 => &["X", "Y", "SECS"],
            Self::Glide2 => &["TO", "SECS"],
            Self::GlideToRandomPosition => &["SECS"],
            Self::GlideToMousePointer => &["SECS"],
            Self::PointInDirection => &["DIRECTION"],
            Self::PointTowardsMousePointer => &[],
            Self::PointTowardsRandomDirection => &[],
            Self::PointTowards => &["TOWARDS"],
            Self::ChangeX => &["DX"],
            Self::SetX => &["X"],
            Self::ChangeY => &["DY"],
            Self::SetY => &["Y"],
            Self::IfOnEdgeBounce => &[],
            Self::SetRotationStyleLeftRight => &[],
            Self::SetRotationStyleDoNotRotate => &[],
            Self::SetRotationStyleAllAround => &[],
            Self::Say2 => &["MESSAGE", "SECS"],
            Self::Say1 => &["MESSAGE"],
            Self::Think2 => &["MESSAGE", "SECS"],
            Self::Think1 => &["MESSAGE"],
            Self::SwitchCostume => &["COSTUME"],
            Self::NextCostume => &[],
            Self::SwitchBackdrop => &["BACKDROP"],
            Self::PreviousBackdrop => &[],
            Self::RandomBackdrop => &[],
            Self::NextBackdrop => &[],
            Self::SetSize => &["SIZE"],
            Self::ChangeSize => &["CHANGE"],
            Self::ChangeColorEffect => &["CHANGE"],
            Self::ChangeFisheyeEffect => &["CHANGE"],
            Self::ChangeWhirlEffect => &["CHANGE"],
            Self::ChangePixelateEffect => &["CHANGE"],
            Self::ChangeMosaicEffect => &["CHANGE"],
            Self::ChangeBrightnessEffect => &["CHANGE"],
            Self::ChangeGhostEffect => &["CHANGE"],
            Self::SetColorEffect => &["VALUE"],
            Self::SetFisheyeEffect => &["VALUE"],
            Self::SetWhirlEffect => &["VALUE"],
            Self::SetPixelateEffect => &["VALUE"],
            Self::SetMosaicEffect => &["VALUE"],
            Self::SetBrightnessEffect => &["VALUE"],
            Self::SetGhostEffect => &["VALUE"],
            Self::ClearGraphicEffects => &[],
            Self::Show => &[],
            Self::Hide => &[],
            Self::GotoFront => &[],
            Self::GotoBack => &[],
            Self::GoForward => &["NUM"],
            Self::GoBackward => &["NUM"],
            Self::PlaySoundUntilDone => &["SOUND_MENU"],
            Self::StartSound => &["SOUND_MENU"],
            Self::StopAllSounds => &[],
            Self::ChangePitchEffect => &["VALUE"],
            Self::ChangePanEffect => &["VALUE"],
            Self::SetPitchEffect => &["VALUE"],
            Self::SetPanEffect => &["VALUE"],
            Self::ChangeVolume => &["VOLUME"],
            Self::SetVolume => &["VOLUME"],
            Self::ClearSoundEffects => &[],
            Self::Broadcast => &["BROADCAST_INPUT"],
            Self::BroadcastAndWait => &["BROADCAST_INPUT"],
            Self::Wait => &["DURATION"],
            Self::StopAll => &[],
            Self::StopThisScript => &[],
            Self::StopOtherScripts => &[],
            Self::DeleteThisClone => &[],
            Self::Clone0 => &[],
            Self::Clone1 => &["CLONE_OPTION"],
            Self::Ask => &["QUESTION"],
            Self::SetDragModeDraggable => &[],
            Self::SetDragModeNotDraggable => &[],
            Self::ResetTimer => &[],
            Self::EraseAll => &[],
            Self::Stamp => &[],
            Self::PenDown => &[],
            Self::PenUp => &[],
            Self::SetPenColor => &["COLOR"],
            Self::ChangePenSize => &["SIZE"],
            Self::SetPenSize => &["SIZE"],
            Self::SetPenHue => &["VALUE"],
            Self::SetPenSaturation => &["VALUE"],
            Self::SetPenBrightness => &["VALUE"],
            Self::SetPenTransparency => &["VALUE"],
            Self::ChangePenHue => &["VALUE"],
            Self::ChangePenSaturation => &["VALUE"],
            Self::ChangePenBrightness => &["VALUE"],
            Self::ChangePenTransparency => &["VALUE"],
            Self::Rest => &["BEATS"],
            Self::SetTempo => &["TEMPO"],
            Self::ChangeTempo => &["TEMPO"],
        }
    }

    pub fn fields(&self) -> Option<&'static str> {
        match self {
            Self::Move => None,
            Self::TurnLeft => None,
            Self::TurnRight => None,
            Self::GotoRandomPosition => None,
            Self::GotoMousePointer => None,
            Self::Goto1 => None,
            Self::Goto2 => None,
            Self::Glide3 => None,
            Self::Glide2 => None,
            Self::GlideToRandomPosition => None,
            Self::GlideToMousePointer => None,
            Self::PointInDirection => None,
            Self::PointTowardsMousePointer => None,
            Self::PointTowardsRandomDirection => None,
            Self::PointTowards => None,
            Self::ChangeX => None,
            Self::SetX => None,
            Self::ChangeY => None,
            Self::SetY => None,
            Self::IfOnEdgeBounce => None,
            Self::SetRotationStyleLeftRight => Some("{\"STYLE\": [\"left-right\", null]}"),
            Self::SetRotationStyleDoNotRotate => Some("{\"STYLE\": [\"don't rotate\", null]}"),
            Self::SetRotationStyleAllAround => Some("{\"STYLE\": [\"all around\", null]}"),
            Self::Say2 => None,
            Self::Say1 => None,
            Self::Think2 => None,
            Self::Think1 => None,
            Self::SwitchCostume => None,
            Self::NextCostume => None,
            Self::SwitchBackdrop => None,
            Self::PreviousBackdrop => None,
            Self::RandomBackdrop => None,
            Self::NextBackdrop => None,
            Self::SetSize => None,
            Self::ChangeSize => None,
            Self::ChangeColorEffect => Some("{\"EFFECT\": [\"COLOR\", null]}"),
            Self::ChangeFisheyeEffect => Some("{\"EFFECT\": [\"FISHEYE\", null]}"),
            Self::ChangeWhirlEffect => Some("{\"EFFECT\": [\"WHIRL\", null]}"),
            Self::ChangePixelateEffect => Some("{\"EFFECT\": [\"PIXELATE\", null]}"),
            Self::ChangeMosaicEffect => Some("{\"EFFECT\": [\"MOSAIC\", null]}"),
            Self::ChangeBrightnessEffect => Some("{\"EFFECT\": [\"BRIGHTNESS\", null]}"),
            Self::ChangeGhostEffect => Some("{\"EFFECT\": [\"GHOST\", null]}"),
            Self::SetColorEffect => Some("{\"EFFECT\": [\"COLOR\", null]}"),
            Self::SetFisheyeEffect => Some("{\"EFFECT\": [\"FISHEYE\", null]}"),
            Self::SetWhirlEffect => Some("{\"EFFECT\": [\"WHIRL\", null]}"),
            Self::SetPixelateEffect => Some("{\"EFFECT\": [\"PIXELATE\", null]}"),
            Self::SetMosaicEffect => Some("{\"EFFECT\": [\"MOSAIC\", null]}"),
            Self::SetBrightnessEffect => Some("{\"EFFECT\": [\"BRIGHTNESS\", null]}"),
            Self::SetGhostEffect => Some("{\"EFFECT\": [\"GHOST\", null]}"),
            Self::ClearGraphicEffects => None,
            Self::Show => None,
            Self::Hide => None,
            Self::GotoFront => Some("{\"FRONT_BACK\": [\"front\", null]}"),
            Self::GotoBack => Some("{\"FRONT_BACK\": [\"back\", null]}"),
            Self::GoForward => Some("{\"FORWARD_BACKWARD\": [\"forward\", null]}"),
            Self::GoBackward => Some("{\"FORWARD_BACKWARD\": [\"backward\", null]}"),
            Self::PlaySoundUntilDone => None,
            Self::StartSound => None,
            Self::StopAllSounds => None,
            Self::ChangePitchEffect => Some("{\"EFFECT\": [\"PITCH\", null]}"),
            Self::ChangePanEffect => Some("{\"EFFECT\": [\"PAN\", null]}"),
            Self::SetPitchEffect => Some("{\"EFFECT\": [\"PITCH\", null]}"),
            Self::SetPanEffect => Some("{\"EFFECT\": [\"PAN\", null]}"),
            Self::ChangeVolume => None,
            Self::SetVolume => None,
            Self::ClearSoundEffects => None,
            Self::Broadcast => None,
            Self::BroadcastAndWait => None,
            Self::Wait => None,
            Self::StopAll => Some("{\"STOP_OPTION\": [\"all\", null]}"),
            Self::StopThisScript => Some("{\"STOP_OPTION\": [\"this script\", null]}"),
            Self::StopOtherScripts => {
                Some("{\"STOP_OPTION\": [\"other scripts in sprite\", null]}")
            }
            Self::DeleteThisClone => None,
            Self::Clone0 => None,
            Self::Clone1 => None,
            Self::Ask => None,
            Self::SetDragModeDraggable => Some("{\"DRAG_MODE\": [\"draggable\", null]}"),
            Self::SetDragModeNotDraggable => Some("{\"DRAG_MODE\": [\"not draggable\", null]}"),
            Self::ResetTimer => None,
            Self::EraseAll => None,
            Self::Stamp => None,
            Self::PenDown => None,
            Self::PenUp => None,
            Self::SetPenColor => None,
            Self::ChangePenSize => None,
            Self::SetPenSize => None,
            Self::SetPenHue => None,
            Self::SetPenSaturation => None,
            Self::SetPenBrightness => None,
            Self::SetPenTransparency => None,
            Self::ChangePenHue => None,
            Self::ChangePenSaturation => None,
            Self::ChangePenBrightness => None,
            Self::ChangePenTransparency => None,
            Self::Rest => None,
            Self::SetTempo => None,
            Self::ChangeTempo => None,
        }
    }
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum Repr {
    XPosition,
    YPosition,
    Direction,
    Size,
    CostumeNumber,
    CostumeName,
    BackdropNumber,
    BackdropName,
    Volume,
    DistanceToMousePointer,
    DistanceTo,
    TouchingMousePointer,
    TouchingEdge,
    Touching,
    KeyPressed,
    MouseDown,
    MouseX,
    MouseY,
    Loudness,
    Timer,
    CurrentYear,
    CurrentMonth,
    CurrentDate,
    CurrentDayOfWeek,
    CurrentHour,
    CurrentMinute,
    CurrentSecond,
    DaysSince2000,
    Username,
    TouchingColor,
    ColorIsTouchingColor,
    Answer,
    Random,
    Contains,
}

impl Repr {
    pub fn menu(&self) -> Option<Menu> {
        match self {
            Self::DistanceToMousePointer => Some(Menu {
                opcode: "sensing_distancetomenu",
                input: "DISTANCETOMENU",
                field: "DISTANCETOMENU",
                default: "_mouse_",
            }),
            Self::DistanceTo => Some(Menu {
                opcode: "sensing_distancetomenu",
                input: "DISTANCETOMENU",
                field: "DISTANCETOMENU",
                default: "_mouse_",
            }),
            Self::TouchingMousePointer => Some(Menu {
                opcode: "sensing_touchingobjectmenu",
                input: "TOUCHINGOBJECTMENU",
                field: "TOUCHINGOBJECTMENU",
                default: "_mouse_",
            }),
            Self::TouchingEdge => Some(Menu {
                opcode: "sensing_touchingobjectmenu",
                input: "TOUCHINGOBJECTMENU",
                field: "TOUCHINGOBJECTMENU",
                default: "_edge_",
            }),
            Self::Touching => Some(Menu {
                opcode: "sensing_touchingobjectmenu",
                input: "TOUCHINGOBJECTMENU",
                field: "TOUCHINGOBJECTMENU",
                default: "_mouse_",
            }),
            Self::KeyPressed => Some(Menu {
                opcode: "sensing_keyoptions",
                input: "KEY_OPTION",
                field: "KEY_OPTION",
                default: "any",
            }),
            _ => None,
        }
    }

    pub fn overloads(_name: &str) -> &'static [Self] {
        &[]
    }

    pub fn from_shape(name: &str, args: usize) -> Option<Self> {
        match (name, args) {
            ("x_position", _) => Some(Self::XPosition),
            ("y_position", _) => Some(Self::YPosition),
            ("direction", _) => Some(Self::Direction),
            ("size", _) => Some(Self::Size),
            ("costume_number", _) => Some(Self::CostumeNumber),
            ("costume_name", _) => Some(Self::CostumeName),
            ("backdrop_number", _) => Some(Self::BackdropNumber),
            ("backdrop_name", _) => Some(Self::BackdropName),
            ("volume", _) => Some(Self::Volume),
            ("distance_to_mouse_pointer", _) => Some(Self::DistanceToMousePointer),
            ("distance_to", _) => Some(Self::DistanceTo),
            ("touching_mouse_pointer", _) => Some(Self::TouchingMousePointer),
            ("touching_edge", _) => Some(Self::TouchingEdge),
            ("touching", _) => Some(Self::Touching),
            ("key_pressed", _) => Some(Self::KeyPressed),
            ("mouse_down", _) => Some(Self::MouseDown),
            ("mouse_x", _) => Some(Self::MouseX),
            ("mouse_y", _) => Some(Self::MouseY),
            ("loudness", _) => Some(Self::Loudness),
            ("timer", _) => Some(Self::Timer),
            ("current_year", _) => Some(Self::CurrentYear),
            ("current_month", _) => Some(Self::CurrentMonth),
            ("current_date", _) => Some(Self::CurrentDate),
            ("current_day_of_week", _) => Some(Self::CurrentDayOfWeek),
            ("current_hour", _) => Some(Self::CurrentHour),
            ("current_minute", _) => Some(Self::CurrentMinute),
            ("current_second", _) => Some(Self::CurrentSecond),
            ("days_since_2000", _) => Some(Self::DaysSince2000),
            ("username", _) => Some(Self::Username),
            ("touching_color", _) => Some(Self::TouchingColor),
            ("color_is_touching_color", _) => Some(Self::ColorIsTouchingColor),
            ("answer", _) => Some(Self::Answer),
            ("random", _) => Some(Self::Random),
            ("contains", _) => Some(Self::Contains),
            _ => None,
        }
    }

    pub fn name(&self) -> &'static str {
        match self {
            Self::XPosition => "x_position",
            Self::YPosition => "y_position",
            Self::Direction => "direction",
            Self::Size => "size",
            Self::CostumeNumber => "costume_number",
            Self::CostumeName => "costume_name",
            Self::BackdropNumber => "backdrop_number",
            Self::BackdropName => "backdrop_name",
            Self::Volume => "volume",
            Self::DistanceToMousePointer => "distance_to_mouse_pointer",
            Self::DistanceTo => "distance_to",
            Self::TouchingMousePointer => "touching_mouse_pointer",
            Self::TouchingEdge => "touching_edge",
            Self::Touching => "touching",
            Self::KeyPressed => "key_pressed",
            Self::MouseDown => "mouse_down",
            Self::MouseX => "mouse_x",
            Self::MouseY => "mouse_y",
            Self::Loudness => "loudness",
            Self::Timer => "timer",
            Self::CurrentYear => "current_year",
            Self::CurrentMonth => "current_month",
            Self::CurrentDate => "current_date",
            Self::CurrentDayOfWeek => "current_day_of_week",
            Self::CurrentHour => "current_hour",
            Self::CurrentMinute => "current_minute",
            Self::CurrentSecond => "current_second",
            Self::DaysSince2000 => "days_since_2000",
            Self::Username => "username",
            Self::TouchingColor => "touching_color",
            Self::ColorIsTouchingColor => "color_is_touching_color",
            Self::Answer => "answer",
            Self::Random => "random",
            Self::Contains => "contains",
        }
    }

    pub fn all_names() -> &'static [&'static str] {
        &[
            "x_position",
            "y_position",
            "direction",
            "size",
            "costume_number",
            "costume_name",
            "backdrop_number",
            "backdrop_name",
            "volume",
            "distance_to_mouse_pointer",
            "distance_to",
            "touching_mouse_pointer",
            "touching_edge",
            "touching",
            "key_pressed",
            "mouse_down",
            "mouse_x",
            "mouse_y",
            "loudness",
            "timer",
            "current_year",
            "current_month",
            "current_date",
            "current_day_of_week",
            "current_hour",
            "current_minute",
            "current_second",
            "days_since_2000",
            "username",
            "touching_color",
            "color_is_touching_color",
            "answer",
            "random",
            "contains",
        ]
    }

    pub fn opcode(&self) -> &'static str {
        match self {
            Self::XPosition => "motion_xposition",
            Self::YPosition => "motion_yposition",
            Self::Direction => "motion_direction",
            Self::Size => "looks_size",
            Self::CostumeNumber => "looks_costumenumbername",
            Self::CostumeName => "looks_costumenumbername",
            Self::BackdropNumber => "looks_backdropnumbername",
            Self::BackdropName => "looks_backdropnumbername",
            Self::Volume => "sound_volume",
            Self::DistanceToMousePointer => "sensing_distanceto",
            Self::DistanceTo => "sensing_distanceto",
            Self::TouchingMousePointer => "sensing_touchingobject",
            Self::TouchingEdge => "sensing_touchingobject",
            Self::Touching => "sensing_touchingobject",
            Self::KeyPressed => "sensing_keypressed",
            Self::MouseDown => "sensing_mousedown",
            Self::MouseX => "sensing_mousex",
            Self::MouseY => "sensing_mousey",
            Self::Loudness => "sensing_loudness",
            Self::Timer => "sensing_timer",
            Self::CurrentYear => "sensing_current",
            Self::CurrentMonth => "sensing_current",
            Self::CurrentDate => "sensing_current",
            Self::CurrentDayOfWeek => "sensing_current",
            Self::CurrentHour => "sensing_current",
            Self::CurrentMinute => "sensing_current",
            Self::CurrentSecond => "sensing_current",
            Self::DaysSince2000 => "sensing_dayssince2000",
            Self::Username => "sensing_username",
            Self::TouchingColor => "sensing_touchingcolor",
            Self::ColorIsTouchingColor => "sensing_coloristouchingcolor",
            Self::Answer => "sensing_answer",
            Self::Random => "operator_random",
            Self::Contains => "operator_contains",
        }
    }

    pub fn args(&self) -> &'static [&'static str] {
        match self {
            Self::XPosition => &[],
            Self::YPosition => &[],
            Self::Direction => &[],
            Self::Size => &[],
            Self::CostumeNumber => &[],
            Self::CostumeName => &[],
            Self::BackdropNumber => &[],
            Self::BackdropName => &[],
            Self::Volume => &[],
            Self::DistanceToMousePointer => &[],
            Self::DistanceTo => &["DISTANCETOMENU"],
            Self::TouchingMousePointer => &[],
            Self::TouchingEdge => &[],
            Self::Touching => &["TOUCHINGOBJECTMENU"],
            Self::KeyPressed => &["KEY_OPTION"],
            Self::MouseDown => &[],
            Self::MouseX => &[],
            Self::MouseY => &[],
            Self::Loudness => &[],
            Self::Timer => &[],
            Self::CurrentYear => &[],
            Self::CurrentMonth => &[],
            Self::CurrentDate => &[],
            Self::CurrentDayOfWeek => &[],
            Self::CurrentHour => &[],
            Self::CurrentMinute => &[],
            Self::CurrentSecond => &[],
            Self::DaysSince2000 => &[],
            Self::Username => &[],
            Self::TouchingColor => &["COLOR"],
            Self::ColorIsTouchingColor => &["COLOR", "COLOR2"],
            Self::Answer => &[],
            Self::Random => &["FROM", "TO"],
            Self::Contains => &["STRING1", "STRING2"],
        }
    }

    pub fn fields(&self) -> Option<&'static str> {
        match self {
            Self::XPosition => None,
            Self::YPosition => None,
            Self::Direction => None,
            Self::Size => None,
            Self::CostumeNumber => Some("{\"NUMBER_NAME\": [\"number\", null]}"),
            Self::CostumeName => Some("{\"NUMBER_NAME\": [\"name\", null]}"),
            Self::BackdropNumber => Some("{\"NUMBER_NAME\": [\"number\", null]}"),
            Self::BackdropName => Some("{\"NUMBER_NAME\": [\"name\", null]}"),
            Self::Volume => None,
            Self::DistanceToMousePointer => None,
            Self::DistanceTo => None,
            Self::TouchingMousePointer => None,
            Self::TouchingEdge => None,
            Self::Touching => None,
            Self::KeyPressed => None,
            Self::MouseDown => None,
            Self::MouseX => None,
            Self::MouseY => None,
            Self::Loudness => None,
            Self::Timer => None,
            Self::CurrentYear => Some("{\"CURRENTMENU\": [\"YEAR\", null]}"),
            Self::CurrentMonth => Some("{\"CURRENTMENU\": [\"MONTH\", null]}"),
            Self::CurrentDate => Some("{\"CURRENTMENU\": [\"DATE\", null]}"),
            Self::CurrentDayOfWeek => Some("{\"CURRENTMENU\": [\"DAYOFWEEK\", null]}"),
            Self::CurrentHour => Some("{\"CURRENTMENU\": [\"HOUR\", null]}"),
            Self::CurrentMinute => Some("{\"CURRENTMENU\": [\"MINUTE\", null]}"),
            Self::CurrentSecond => Some("{\"CURRENTMENU\": [\"SECOND\", null]}"),
            Self::DaysSince2000 => None,
            Self::Username => None,
            Self::TouchingColor => None,
            Self::ColorIsTouchingColor => None,
            Self::Answer => None,
            Self::Random => None,
            Self::Contains => None,
        }
    }
}
